import hashlib
import random
import string
import time
from urllib import parse

import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
import requests
from locust import SequentialTaskSet, HttpUser, between, task, tag

HOST = "http://xxx.xxx.xxx.net"
class PTUtils(object):
    jf_app = {
        "app_secret": "",
        "app_id": "",
        "type": ""
    }

    user_info = {
        "username": "",
        "password": "",
        "uid": ""
    }

    def get_pt_sign(self, app, params_dict):
        """sign签名"""
        # step0：将非空参数按key进行ASCII字典升序排列, 再将排序后的参数(key=value)以&拼接起来
        key_lists = sorted([key for key in params_dict.keys() if params_dict.get(key) or params_dict.get(key) == 0])
        sign_raw = "&".join(list(map(lambda key: key + "=" + str(params_dict.get(key)), key_lists)))
        # step1:用&拼接上md5(app_secret + app_id)生成的小写字符串
        secret_key = hashlib.md5((app["app_secret"] + app["app_id"]).encode(encoding='UTF-8')).hexdigest()
        sign_raw = sign_raw + "&" + secret_key
        # step2:将签名原串 url编码
        sign_raw = parse.quote_plus(sign_raw, safe="*")
        # step3:再对整个字符串进行md5加密, 生成小写的签名字符串
        signature = hashlib.md5(sign_raw.encode(encoding='UTF-8')).hexdigest()
        return signature

    def jf_access_token(self):
        """应用令牌access_token"""
        params = {
            "app_id": self.jf_app.get("app_id"),
            "app_secret": self.jf_app.get("app_secret"),
            "platform": "1",
            "timestamp": str(int(time.time()))
        }
        params["sign"] = self.get_pt_sign(self.jf_app, params)
        resp = requests.post(HOST+"/v1/oauth2/access_token", params)
        if resp.status_code != 200 or resp.json().get("code") != 1001:
            print("获取积分商场应用令牌失败:", resp.text)
        access_token = resp.json()["data"]["access_token"]
        return access_token

    def user_access_token_jf(self):
        """用户令牌：用户通过应用令牌登陆认证"""
        params = {
            "access_token": self.jf_access_token(),
            "username": self.user_info.get("username"),
            "password": self.user_info.get("password"),
            "platform": "1",
            "timestamp": str(int(time.time()))
        }
        params["sign"] = self.get_pt_sign(self.jf_app, params)
        resp = requests.post(HOST+"/v1/auth/login", params)
        if resp.status_code != 200 or resp.json().get("code") != 1001:
            print("获取用户令牌失败:", resp.text)
        access_token = resp.json()["data"]["access_token"]
        return access_token


class TaskSuit(SequentialTaskSet):
    """ 虚拟货币中心-预冻结与冻结时，并发引起入库主键冲突，为此优化后针对该场景进行并发测试"""
    _pt = PTUtils()

    def on_start(self):
        self.headers = {"Content-Type": "application/json"}
        self.access_token = self._pt.user_access_token_jf()

    def on_stop(self):
        pass

    @task
    @tag("coin_settle")
    def pad_apply_case(self):
        url = '/v1/coin/pad/apply'
        num = string.ascii_letters + string.digits
        params = {
            "access_token": self.access_token,
            "coin_id": "1559038086",
            "amount": 1,
            "expire": 1,
            "remark": "MT-申请预授权",
            "out_trade_no": "MTTES" + str(int(time.time())) + "".join(random.sample(num, 16)),
            "timestamp": str(int(time.time()))
        }
        params["sign"] = self._pt.get_pt_sign(self._pt.jf_app, params)

        with self.client.post(url, data=params, catch_response=True, name="pad_apply") as rsp:
            if rsp.status_code == 200 and rsp.json()['code'] == 1001:
                pad_id = rsp.json().get("data").get("id")
                self.pad_id = pad_id
                rsp.success()
            else:
                print("预授权失败:",rsp.text)
                rsp.failure('/v1/coin/pad/apply 预授权失败！')

    @task
    @tag("coin_settle")
    def pad_settle_case(self):
        url = '/v1/p/coin/pad/settle'
        params = {
            "pad_id": self.pad_id,
            "user_id": "33970",
            "coin_id": "1559038086",
            "timestamp": str(int(time.time())),
            "amount": 1,  # 结算金额，默认全部，可分次结算，每次结算一部分金额
            "remark": "结算"
        }
        params["sign"] = self._pt.get_pt_sign(self._pt.p_app, params)
        with self.client.post(url, data=params, catch_response=True, name="pad_settle") as rsp:
            # 提取响应json 中的信息，定义为 类变量
            if rsp.status_code == 200 and rsp.json()['code'] == 1001:
                rsp.success()
            else:
                print("结算失败:",rsp.text)
                rsp.failure('/v1/p/coin/pad/settle 结算失败！')


class TaskPlan(HttpUser):
    tasks = [TaskSuit]
    wait_time = between(0.01, 3)
    host = HOST


"""
运行命令：
locust -h
locust -f locust_coin_api_perf.py
locust  --step-load   --step-users  10 --step-time 5s  -f locust_coin_api_perf.py
"""
